mirror of
https://github.com/PluralKit/PluralKit.git
synced 2026-02-09 15:27:54 +00:00
Merge branch 'feature/granular-privacy'
This commit is contained in:
commit
ff7b728762
39 changed files with 692 additions and 164 deletions
|
|
@ -279,6 +279,9 @@ namespace PluralKit.Bot
|
|||
public LookupContext LookupContextFor(SystemId systemId) =>
|
||||
System?.Id == systemId ? LookupContext.ByOwner : LookupContext.ByNonOwner;
|
||||
|
||||
public LookupContext LookupContextFor(PKMember target) =>
|
||||
System?.Id == target.System ? LookupContext.ByOwner : LookupContext.ByNonOwner;
|
||||
|
||||
public Context CheckSystemPrivacy(PKSystem target, PrivacyLevel level)
|
||||
{
|
||||
if (level.CanAccess(LookupContextFor(target))) return this;
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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`.");
|
||||
|
|
|
|||
|
|
@ -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.");
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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?");
|
||||
|
|
|
|||
|
|
@ -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}.");
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -68,11 +68,11 @@ namespace PluralKit.Bot {
|
|||
public static PKError MemberLinkCancelled => new PKError("Member link cancelled.");
|
||||
public static PKError MemberUnlinkCancelled => new PKError("Member unlink cancelled.");
|
||||
|
||||
public static PKError SameSwitch(ICollection<PKMember> members)
|
||||
public static PKError SameSwitch(ICollection<PKMember> members, LookupContext ctx)
|
||||
{
|
||||
if (members.Count == 0) return new PKError("There's already no one in front.");
|
||||
if (members.Count == 1) return new PKError($"Member {members.First().Name.SanitizeMentions()} is already fronting.");
|
||||
return new PKError($"Members {string.Join(", ", members.Select(m => m.Name.SanitizeMentions()))} are already fronting.");
|
||||
if (members.Count == 1) return new PKError($"Member {members.First().NameFor(ctx).SanitizeMentions()} is already fronting.");
|
||||
return new PKError($"Members {string.Join(", ", members.Select(m => m.NameFor(ctx).SanitizeMentions()))} are already fronting.");
|
||||
}
|
||||
|
||||
public static PKError DuplicateSwitchMembers => new PKError("Duplicate members in member list.");
|
||||
|
|
|
|||
|
|
@ -108,14 +108,14 @@ namespace PluralKit.Bot
|
|||
{
|
||||
// If the system has pings enabled, go ahead
|
||||
var embed = new DiscordEmbedBuilder().WithDescription($"[Jump to pinged message]({evt.Message.JumpLink})");
|
||||
await evt.Channel.SendMessageAsync($"Psst, **{msg.Member.DisplayName ?? msg.Member.Name}** (<@{msg.Message.Sender}>), you have been pinged by <@{evt.User.Id}>.", embed: embed.Build());
|
||||
await evt.Channel.SendMessageAsync($"Psst, **{msg.Member.DisplayName()}** (<@{msg.Message.Sender}>), you have been pinged by <@{evt.User.Id}>.", embed: embed.Build());
|
||||
}
|
||||
else
|
||||
{
|
||||
// If not, tell them in DMs (if we can)
|
||||
try
|
||||
{
|
||||
await guildUser.SendMessageAsync($"{Emojis.Error} {msg.Member.DisplayName ?? msg.Member.Name}'s system has disabled reaction pings. If you want to mention them anyway, you can copy/paste the following message:");
|
||||
await guildUser.SendMessageAsync($"{Emojis.Error} {msg.Member.DisplayName()}'s system has disabled reaction pings. If you want to mention them anyway, you can copy/paste the following message:");
|
||||
await guildUser.SendMessageAsync($"`<@{msg.Message.Sender}>`");
|
||||
}
|
||||
catch (UnauthorizedException) { }
|
||||
|
|
|
|||
|
|
@ -11,6 +11,6 @@ namespace PluralKit.Bot
|
|||
public interface IListRenderer
|
||||
{
|
||||
int MembersPerPage { get; }
|
||||
void RenderPage(DiscordEmbedBuilder eb, DateTimeZone zone, IEnumerable<ListedMember> members);
|
||||
void RenderPage(DiscordEmbedBuilder eb, DateTimeZone zone, IEnumerable<ListedMember> members, LookupContext ctx);
|
||||
}
|
||||
}
|
||||
|
|
@ -20,25 +20,25 @@ namespace PluralKit.Bot
|
|||
_fields = fields;
|
||||
}
|
||||
|
||||
public void RenderPage(DiscordEmbedBuilder eb, DateTimeZone zone, IEnumerable<ListedMember> members)
|
||||
public void RenderPage(DiscordEmbedBuilder eb, DateTimeZone zone, IEnumerable<ListedMember> members, LookupContext ctx)
|
||||
{
|
||||
string FormatTimestamp(Instant timestamp) => DateTimeFormats.ZonedDateTimeFormat.Format(timestamp.InZone(zone));
|
||||
|
||||
foreach (var m in members)
|
||||
{
|
||||
var profile = $"**ID**: {m.Hid}";
|
||||
if (_fields.ShowDisplayName && m.DisplayName != null) profile += $"\n**Display name**: {m.DisplayName}";
|
||||
if (_fields.ShowPronouns && m.Pronouns != null) profile += $"\n**Pronouns**: {m.Pronouns}";
|
||||
if (_fields.ShowBirthday && m.Birthday != null) profile += $"\n**Birthdate**: {m.BirthdayString}";
|
||||
if (_fields.ShowDisplayName && m.DisplayName != null && m.NamePrivacy.CanAccess(ctx)) profile += $"\n**Display name**: {m.DisplayName}";
|
||||
if (_fields.ShowPronouns && m.Pronouns != null && m.PronounPrivacy.CanAccess(ctx)) profile += $"\n**Pronouns**: {m.Pronouns}";
|
||||
if (_fields.ShowBirthday && m.Birthday != null && m.BirthdayPrivacy.CanAccess(ctx)) profile += $"\n**Birthdate**: {m.BirthdayString}";
|
||||
if (_fields.ShowProxyTags && m.ProxyTags.Count > 0) profile += $"\n**Proxy tags:** {m.ProxyTagsString()}";
|
||||
if (_fields.ShowMessageCount && m.MessageCount > 0) profile += $"\n**Message count:** {m.MessageCount}";
|
||||
if (_fields.ShowLastMessage && m.LastMessage != null) profile += $"\n**Last message:** {FormatTimestamp(DiscordUtils.SnowflakeToInstant(m.LastMessage.Value))}";
|
||||
if (_fields.ShowLastSwitch && m.LastSwitchTime != null) profile += $"\n**Last switched in:** {FormatTimestamp(m.LastSwitchTime.Value)}";
|
||||
if (_fields.ShowDescription && m.Description != null) profile += $"\n\n{m.Description}";
|
||||
if (_fields.ShowPrivacy && m.MemberPrivacy == PrivacyLevel.Private)
|
||||
profile += "\n*(this member is private)*";
|
||||
if (_fields.ShowMessageCount && m.MessageCount > 0 && m.MetadataPrivacy.CanAccess(ctx)) profile += $"\n**Message count:** {m.MessageCount}";
|
||||
if (_fields.ShowLastMessage && m.LastMessage != null && m.MetadataPrivacy.CanAccess(ctx)) profile += $"\n**Last message:** {FormatTimestamp(DiscordUtils.SnowflakeToInstant(m.LastMessage.Value))}";
|
||||
if (_fields.ShowLastSwitch && m.LastSwitchTime != null && m.MetadataPrivacy.CanAccess(ctx)) profile += $"\n**Last switched in:** {FormatTimestamp(m.LastSwitchTime.Value)}";
|
||||
if (_fields.ShowDescription && m.Description != null && m.DescriptionPrivacy.CanAccess(ctx)) profile += $"\n\n{m.Description}";
|
||||
if (_fields.ShowPrivacy && m.MemberVisibility == PrivacyLevel.Private)
|
||||
profile += "\n*(this member is hidden)*";
|
||||
|
||||
eb.AddField(m.Name, profile.Truncate(1024));
|
||||
eb.AddField(m.NameFor(ctx), profile.Truncate(1024));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ namespace PluralKit.Bot
|
|||
{
|
||||
public int MembersPerPage => 25;
|
||||
|
||||
public void RenderPage(DiscordEmbedBuilder eb, DateTimeZone timezone, IEnumerable<ListedMember> members)
|
||||
public void RenderPage(DiscordEmbedBuilder eb, DateTimeZone timezone, IEnumerable<ListedMember> members, LookupContext ctx)
|
||||
{
|
||||
string RenderLine(ListedMember m)
|
||||
{
|
||||
|
|
@ -22,11 +22,10 @@ namespace PluralKit.Bot
|
|||
var proxyTagsString = m.ProxyTagsString().SanitizeMentions();
|
||||
if (proxyTagsString.Length > 100) // arbitrary threshold for now, tweak?
|
||||
proxyTagsString = "tags too long, see member card";
|
||||
|
||||
return $"[`{m.Hid}`] **{m.Name.SanitizeMentions()}** *({proxyTagsString})*";
|
||||
return $"[`{m.Hid}`] **{m.NameFor(ctx).SanitizeMentions()}** *({proxyTagsString})*";
|
||||
}
|
||||
|
||||
return $"[`{m.Hid}`] **{m.Name.SanitizeMentions()}**";
|
||||
return $"[`{m.Hid}`] **{m.NameFor(ctx).SanitizeMentions()}**";
|
||||
}
|
||||
|
||||
var buf = new StringBuilder();
|
||||
|
|
|
|||
|
|
@ -52,14 +52,14 @@ namespace PluralKit.Bot
|
|||
return str.ToString();
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<ListedMember>> Execute(IPKConnection conn, PKSystem system)
|
||||
public async Task<IEnumerable<ListedMember>> Execute(IPKConnection conn, PKSystem system, LookupContext ctx)
|
||||
{
|
||||
var filtered = await QueryWithFilter(conn, system);
|
||||
return Sort(filtered);
|
||||
var filtered = await QueryWithFilter(conn, system, ctx);
|
||||
return Sort(filtered, ctx);
|
||||
}
|
||||
|
||||
private Task<IEnumerable<ListedMember>> QueryWithFilter(IPKConnection conn, PKSystem system) =>
|
||||
conn.QueryMemberList(system.Id, PrivacyFilter switch
|
||||
private Task<IEnumerable<ListedMember>> QueryWithFilter(IPKConnection conn, PKSystem system, LookupContext ctx) =>
|
||||
conn.QueryMemberList(system.Id, ctx, PrivacyFilter switch
|
||||
{
|
||||
PrivacyFilter.PrivateOnly => PrivacyLevel.Private,
|
||||
PrivacyFilter.PublicOnly => PrivacyLevel.Public,
|
||||
|
|
@ -67,7 +67,7 @@ namespace PluralKit.Bot
|
|||
_ => throw new ArgumentOutOfRangeException($"Unknown privacy filter {PrivacyFilter}")
|
||||
}, Filter, SearchInDescription);
|
||||
|
||||
private IEnumerable<ListedMember> Sort(IEnumerable<ListedMember> input)
|
||||
private IEnumerable<ListedMember> Sort(IEnumerable<ListedMember> input, LookupContext ctx)
|
||||
{
|
||||
IComparer<T> ReverseMaybe<T>(IComparer<T> c) =>
|
||||
Reverse ? Comparer<T>.Create((a, b) => c.Compare(b, a)) : c;
|
||||
|
|
@ -78,7 +78,7 @@ namespace PluralKit.Bot
|
|||
// As for the OrderByDescending HasValue calls: https://www.jerriepelser.com/blog/orderby-with-null-values/
|
||||
// We want nulls last no matter what, even if orders are reversed
|
||||
SortProperty.Hid => input.OrderBy(m => m.Hid, ReverseMaybe(culture)),
|
||||
SortProperty.Name => input.OrderBy(m => m.Name, ReverseMaybe(culture)),
|
||||
SortProperty.Name => input.OrderBy(m => m.NameFor(ctx), ReverseMaybe(culture)),
|
||||
SortProperty.CreationDate => input.OrderBy(m => m.Created, ReverseMaybe(Comparer<Instant>.Default)),
|
||||
SortProperty.MessageCount => input.OrderByDescending(m => m.MessageCount, ReverseMaybe(Comparer<int>.Default)),
|
||||
SortProperty.DisplayName => input
|
||||
|
|
@ -96,7 +96,7 @@ namespace PluralKit.Bot
|
|||
_ => throw new ArgumentOutOfRangeException($"Unknown sort property {SortProperty}")
|
||||
})
|
||||
// Lastly, add a by-name fallback order for collisions (generally hits w/ lots of null values)
|
||||
.ThenBy(m => m.Name, culture);
|
||||
.ThenBy(m => m.NameFor(ctx), culture);
|
||||
}
|
||||
|
||||
public static SortFilterOptions FromFlags(Context ctx)
|
||||
|
|
|
|||
|
|
@ -26,6 +26,8 @@ namespace PluralKit.Bot {
|
|||
_db = db;
|
||||
}
|
||||
|
||||
|
||||
|
||||
public async Task<DiscordEmbed> CreateSystemEmbed(DiscordClient client, PKSystem system, LookupContext ctx) {
|
||||
var accounts = await _data.GetSystemAccounts(system);
|
||||
|
||||
|
|
@ -45,7 +47,7 @@ namespace PluralKit.Bot {
|
|||
var switchMembers = await _data.GetSwitchMembers(latestSwitch).ToListAsync();
|
||||
if (switchMembers.Count > 0)
|
||||
eb.AddField("Fronter".ToQuantity(switchMembers.Count(), ShowQuantityAs.None),
|
||||
string.Join(", ", switchMembers.Select(m => m.Name)));
|
||||
string.Join(", ", switchMembers.Select(m => m.NameFor(ctx))));
|
||||
}
|
||||
|
||||
if (system.Tag != null) eb.AddField("Tag", system.Tag.EscapeMarkdown());
|
||||
|
|
@ -68,9 +70,10 @@ namespace PluralKit.Bot {
|
|||
public DiscordEmbed CreateLoggedMessageEmbed(PKSystem system, PKMember member, ulong messageId, ulong originalMsgId, DiscordUser sender, string content, DiscordChannel channel) {
|
||||
// TODO: pronouns in ?-reacted response using this card
|
||||
var timestamp = DiscordUtils.SnowflakeToInstant(messageId);
|
||||
var name = member.NameFor(LookupContext.ByNonOwner);
|
||||
return new DiscordEmbedBuilder()
|
||||
.WithAuthor($"#{channel.Name}: {member.Name}", iconUrl: DiscordUtils.WorkaroundForUrlBug(member.AvatarUrl))
|
||||
.WithThumbnailUrl(member.AvatarUrl)
|
||||
.WithAuthor($"#{channel.Name}: {name}", iconUrl: DiscordUtils.WorkaroundForUrlBug(member.AvatarFor(LookupContext.ByNonOwner)))
|
||||
.WithThumbnailUrl(member.AvatarFor(LookupContext.ByNonOwner))
|
||||
.WithDescription(content?.NormalizeLineEndSpacing())
|
||||
.WithFooter($"System ID: {system.Hid} | Member ID: {member.Hid} | Sender: {sender.Username}#{sender.Discriminator} ({sender.Id}) | Message ID: {messageId} | Original Message ID: {originalMsgId}")
|
||||
.WithTimestamp(timestamp.ToDateTimeOffset())
|
||||
|
|
@ -79,8 +82,11 @@ namespace PluralKit.Bot {
|
|||
|
||||
public async Task<DiscordEmbed> CreateMemberEmbed(PKSystem system, PKMember member, DiscordGuild guild, LookupContext ctx)
|
||||
{
|
||||
var name = member.Name;
|
||||
if (system.Name != null) name = $"{member.Name} ({system.Name})";
|
||||
|
||||
// string FormatTimestamp(Instant timestamp) => DateTimeFormats.ZonedDateTimeFormat.Format(timestamp.InZone(system.Zone));
|
||||
|
||||
var name = member.NameFor(ctx);
|
||||
if (system.Name != null) name = $"{name} ({system.Name})";
|
||||
|
||||
DiscordColor color;
|
||||
try
|
||||
|
|
@ -97,20 +103,21 @@ namespace PluralKit.Bot {
|
|||
|
||||
var guildSettings = guild != null ? await _db.Execute(c => c.QueryOrInsertMemberGuildConfig(guild.Id, member.Id)) : null;
|
||||
var guildDisplayName = guildSettings?.DisplayName;
|
||||
var avatar = guildSettings?.AvatarUrl ?? member.AvatarUrl;
|
||||
var avatar = guildSettings?.AvatarUrl ?? member.AvatarFor(ctx);
|
||||
|
||||
var proxyTagsStr = string.Join('\n', member.ProxyTags.Select(t => $"`{t.ProxyString}`"));
|
||||
|
||||
var eb = new DiscordEmbedBuilder()
|
||||
// TODO: add URL of website when that's up
|
||||
.WithAuthor(name, iconUrl: DiscordUtils.WorkaroundForUrlBug(avatar))
|
||||
.WithColor(member.MemberPrivacy.CanAccess(ctx) ? color : DiscordUtils.Gray)
|
||||
.WithFooter($"System ID: {system.Hid} | Member ID: {member.Hid} | Created on {DateTimeFormats.ZonedDateTimeFormat.Format(member.Created.InZone(system.Zone))}");
|
||||
// .WithColor(member.ColorPrivacy.CanAccess(ctx) ? color : DiscordUtils.Gray)
|
||||
.WithColor(color)
|
||||
.WithFooter($"System ID: {system.Hid} | Member ID: {member.Hid} {(member.MetadataPrivacy.CanAccess(ctx) ? $"| Created on {DateTimeFormats.ZonedDateTimeFormat.Format(member.Created.InZone(system.Zone))}":"")}");
|
||||
|
||||
var description = "";
|
||||
if (member.MemberPrivacy == PrivacyLevel.Private) description += "*(this member is private)*\n";
|
||||
if (member.MemberVisibility == PrivacyLevel.Private) description += "*(this member is hidden)*\n";
|
||||
if (guildSettings?.AvatarUrl != null)
|
||||
if (member.AvatarUrl != null)
|
||||
if (member.AvatarFor(ctx) != null)
|
||||
description += $"*(this member has a server-specific avatar set; [click here]({member.AvatarUrl}) to see the global avatar)*\n";
|
||||
else
|
||||
description += "*(this member has a server-specific avatar set)*\n";
|
||||
|
|
@ -118,31 +125,38 @@ namespace PluralKit.Bot {
|
|||
|
||||
if (avatar != null) eb.WithThumbnailUrl(avatar);
|
||||
|
||||
if (!member.DisplayName.EmptyOrNull()) eb.AddField("Display Name", member.DisplayName.Truncate(1024), true);
|
||||
if (!member.DisplayName.EmptyOrNull() && member.NamePrivacy.CanAccess(ctx)) eb.AddField("Display Name", member.DisplayName.Truncate(1024), true);
|
||||
if (guild != null && guildDisplayName != null) eb.AddField($"Server Nickname (for {guild.Name})", guildDisplayName.Truncate(1024), true);
|
||||
if (member.Birthday != null && member.MemberPrivacy.CanAccess(ctx)) eb.AddField("Birthdate", member.BirthdayString, true);
|
||||
if (!member.Pronouns.EmptyOrNull() && member.MemberPrivacy.CanAccess(ctx)) eb.AddField("Pronouns", member.Pronouns.Truncate(1024), true);
|
||||
if (member.MessageCount > 0 && member.MemberPrivacy.CanAccess(ctx)) eb.AddField("Message Count", member.MessageCount.ToString(), true);
|
||||
if (member.Birthday != null && member.BirthdayPrivacy.CanAccess(ctx)) eb.AddField("Birthdate", member.BirthdayString, true);
|
||||
if (!member.Pronouns.EmptyOrNull() && member.PronounPrivacy.CanAccess(ctx)) eb.AddField("Pronouns", member.Pronouns.Truncate(1024), true);
|
||||
if (member.MessageCount > 0 && member.MetadataPrivacy.CanAccess(ctx)) eb.AddField("Message Count", member.MessageCount.ToString(), true);
|
||||
if (member.HasProxyTags) eb.AddField("Proxy Tags", string.Join('\n', proxyTagsStr).Truncate(1024), true);
|
||||
if (!member.Color.EmptyOrNull() && member.MemberPrivacy.CanAccess(ctx)) eb.AddField("Color", $"#{member.Color}", true);
|
||||
if (!member.Description.EmptyOrNull() && member.MemberPrivacy.CanAccess(ctx)) eb.AddField("Description", member.Description.NormalizeLineEndSpacing(), false);
|
||||
// --- For when this gets added to the member object itself or however they get added
|
||||
// if (member.LastMessage != null && member.MetadataPrivacy.CanAccess(ctx)) eb.AddField("Last message:" FormatTimestamp(DiscordUtils.SnowflakeToInstant(m.LastMessage.Value)));
|
||||
// if (member.LastSwitchTime != null && m.MetadataPrivacy.CanAccess(ctx)) eb.AddField("Last switched in:", FormatTimestamp(member.LastSwitchTime.Value));
|
||||
// if (!member.Color.EmptyOrNull() && member.ColorPrivacy.CanAccess(ctx)) eb.AddField("Color", $"#{member.Color}", true);
|
||||
if (!member.Color.EmptyOrNull()) eb.AddField("Color", $"#{member.Color}", true);
|
||||
|
||||
if (!member.Description.EmptyOrNull() && member.DescriptionPrivacy.CanAccess(ctx)) eb.AddField("Description", member.Description.NormalizeLineEndSpacing(), false);
|
||||
|
||||
return eb.Build();
|
||||
}
|
||||
|
||||
public async Task<DiscordEmbed> CreateFronterEmbed(PKSwitch sw, DateTimeZone zone)
|
||||
public async Task<DiscordEmbed> CreateFronterEmbed(PKSwitch sw, DateTimeZone zone, LookupContext ctx)
|
||||
{
|
||||
var members = await _data.GetSwitchMembers(sw).ToListAsync();
|
||||
var timeSinceSwitch = SystemClock.Instance.GetCurrentInstant() - sw.Timestamp;
|
||||
return new DiscordEmbedBuilder()
|
||||
.WithColor(members.FirstOrDefault()?.Color?.ToDiscordColor() ?? DiscordUtils.Gray)
|
||||
.AddField($"Current {"fronter".ToQuantity(members.Count, ShowQuantityAs.None)}", members.Count > 0 ? string.Join(", ", members.Select(m => m.Name)) : "*(no fronter)*")
|
||||
.AddField($"Current {"fronter".ToQuantity(members.Count, ShowQuantityAs.None)}", members.Count > 0 ? string.Join(", ", members.Select(m => m.NameFor(ctx))) : "*(no fronter)*")
|
||||
.AddField("Since", $"{DateTimeFormats.ZonedDateTimeFormat.Format(sw.Timestamp.InZone(zone))} ({DateTimeFormats.DurationFormat.Format(timeSinceSwitch)} ago)")
|
||||
.Build();
|
||||
}
|
||||
|
||||
public async Task<DiscordEmbed> CreateMessageInfoEmbed(DiscordClient client, FullMessage msg)
|
||||
{
|
||||
var ctx = LookupContext.ByNonOwner;
|
||||
|
||||
var channel = await client.GetChannelAsync(msg.Message.Channel);
|
||||
var serverMsg = channel != null ? await channel.GetMessageAsync(msg.Message.Mid) : null;
|
||||
|
||||
|
|
@ -165,12 +179,12 @@ namespace PluralKit.Bot {
|
|||
|
||||
// Put it all together
|
||||
var eb = new DiscordEmbedBuilder()
|
||||
.WithAuthor(msg.Member.Name, iconUrl: DiscordUtils.WorkaroundForUrlBug(msg.Member.AvatarUrl))
|
||||
.WithAuthor(msg.Member.NameFor(ctx), iconUrl: DiscordUtils.WorkaroundForUrlBug(msg.Member.AvatarFor(ctx)))
|
||||
.WithDescription(serverMsg?.Content?.NormalizeLineEndSpacing() ?? "*(message contents deleted or inaccessible)*")
|
||||
.WithImageUrl(serverMsg?.Attachments?.FirstOrDefault()?.Url)
|
||||
.AddField("System",
|
||||
msg.System.Name != null ? $"{msg.System.Name} (`{msg.System.Hid}`)" : $"`{msg.System.Hid}`", true)
|
||||
.AddField("Member", $"{msg.Member.Name} (`{msg.Member.Hid}`)", true)
|
||||
.AddField("Member", $"{msg.Member.NameFor(ctx)} (`{msg.Member.Hid}`)", true)
|
||||
.AddField("Sent by", userStr, inline: true)
|
||||
.WithTimestamp(DiscordUtils.SnowflakeToInstant(msg.Message.Mid).ToDateTimeOffset());
|
||||
|
||||
|
|
@ -181,7 +195,7 @@ namespace PluralKit.Bot {
|
|||
return eb.Build();
|
||||
}
|
||||
|
||||
public Task<DiscordEmbed> CreateFrontPercentEmbed(FrontBreakdown breakdown, DateTimeZone tz)
|
||||
public Task<DiscordEmbed> CreateFrontPercentEmbed(FrontBreakdown breakdown, DateTimeZone tz, LookupContext ctx)
|
||||
{
|
||||
var actualPeriod = breakdown.RangeEnd - breakdown.RangeStart;
|
||||
var eb = new DiscordEmbedBuilder()
|
||||
|
|
@ -200,7 +214,7 @@ namespace PluralKit.Bot {
|
|||
foreach (var pair in membersOrdered)
|
||||
{
|
||||
var frac = pair.Value / actualPeriod;
|
||||
eb.AddField(pair.Key?.Name ?? "*(no fronter)*", $"{frac*100:F0}% ({DateTimeFormats.DurationFormat.Format(pair.Value)})");
|
||||
eb.AddField(pair.Key?.NameFor(ctx) ?? "*(no fronter)*", $"{frac*100:F0}% ({DateTimeFormats.DurationFormat.Format(pair.Value)})");
|
||||
}
|
||||
|
||||
if (membersOrdered.Count > maxEntriesToDisplay)
|
||||
|
|
|
|||
16
PluralKit.Bot/Utils/ModelUtils.cs
Normal file
16
PluralKit.Bot/Utils/ModelUtils.cs
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
using PluralKit.Core;
|
||||
|
||||
namespace PluralKit.Bot
|
||||
{
|
||||
public static class ModelUtils
|
||||
{
|
||||
public static string NameFor(this PKMember member, Context ctx) =>
|
||||
member.NameFor(ctx.LookupContextFor(member));
|
||||
|
||||
public static string AvatarFor(this PKMember member, Context ctx) =>
|
||||
member.AvatarFor(ctx.LookupContextFor(member));
|
||||
|
||||
public static string DisplayName(this PKMember member) =>
|
||||
member.DisplayName ?? member.Name;
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue