2020-03-04 17:13:36 +00:00
using System.Text.RegularExpressions ;
2020-06-17 19:31:39 +00:00
2020-12-25 11:56:46 +00:00
using Myriad.Builders ;
2021-11-27 02:10:56 +00:00
using Myriad.Types ;
2020-02-01 12:03:02 +00:00
2020-06-29 11:57:48 +00:00
using NodaTime ;
2021-10-29 21:44:51 +00:00
using NodaTime.Extensions ;
2020-06-29 11:57:48 +00:00
2020-02-01 12:03:02 +00:00
using PluralKit.Core ;
2021-11-27 02:10:56 +00:00
namespace PluralKit.Bot ;
public class MemberEdit
2020-02-01 12:03:02 +00:00
{
2021-11-27 02:10:56 +00:00
private readonly HttpClient _client ;
private readonly ModelRepository _repo ;
2021-11-27 03:02:58 +00:00
public MemberEdit ( ModelRepository repo , HttpClient client )
2021-11-27 02:10:56 +00:00
{
_repo = repo ;
_client = client ;
}
public async Task Name ( Context ctx , PKMember target )
2020-02-01 12:03:02 +00:00
{
2021-11-27 02:10:56 +00:00
ctx . CheckSystem ( ) . CheckOwnMember ( target ) ;
2020-02-01 12:03:02 +00:00
2021-11-27 02:10:56 +00:00
var newName = ctx . RemainderOrNull ( ) ? ? throw new PKSyntaxError ( "You must pass a new name for the member." ) ;
// Hard name length cap
if ( newName . Length > Limits . MaxMemberNameLength )
throw Errors . StringTooLongError ( "Member name" , newName . Length , Limits . MaxMemberNameLength ) ;
// Warn if there's already a member by this name
var existingMember = await _repo . GetMemberByName ( ctx . System . Id , newName ) ;
if ( existingMember ! = null & & existingMember . Id ! = target . Id )
2020-02-01 12:03:02 +00:00
{
2021-11-27 02:10:56 +00:00
var msg =
$"{Emojis.Warn} You already have a member in your system with the name \" { existingMember . NameFor ( ctx ) } \ " (`{existingMember.Hid}`). Do you want to rename this member to that name too?" ;
if ( ! await ctx . PromptYesNo ( msg , "Rename" ) ) throw new PKError ( "Member renaming cancelled." ) ;
2020-02-01 12:03:02 +00:00
}
2021-11-27 02:10:56 +00:00
// Rename the member
var patch = new MemberPatch { Name = Partial < string > . Present ( newName ) } ;
await _repo . UpdateMember ( target . Id , patch ) ;
await ctx . Reply ( $"{Emojis.Success} Member renamed." ) ;
if ( newName . Contains ( " " ) )
await ctx . Reply (
$"{Emojis.Note} Note that this member's name now contains spaces. You will need to surround it with \" double quotes \ " when using commands referring to it." ) ;
if ( target . DisplayName ! = null )
await ctx . Reply (
$"{Emojis.Note} Note that this member has a display name set ({target.DisplayName}), and will be proxied using that name instead." ) ;
if ( ctx . Guild ! = null )
2020-11-22 21:15:26 +00:00
{
2021-11-27 02:10:56 +00:00
var memberGuildConfig = await _repo . GetMemberGuild ( ctx . Guild . Id , target . Id ) ;
if ( memberGuildConfig . DisplayName ! = null )
await ctx . Reply (
$"{Emojis.Note} Note that this member has a server name set ({memberGuildConfig.DisplayName}) in this server ({ctx.Guild.Name}), and will be proxied using that name here." ) ;
}
}
2021-08-27 15:03:47 +00:00
2021-11-27 02:10:56 +00:00
public async Task Description ( Context ctx , PKMember target )
{
2021-12-07 06:32:29 +00:00
ctx . CheckSystemPrivacy ( target . System , target . DescriptionPrivacy ) ;
2021-11-27 02:10:56 +00:00
var noDescriptionSetMessage = "This member does not have a description set." ;
if ( ctx . System ? . Id = = target . System )
noDescriptionSetMessage + =
$" To set one, type `pk;member {target.Reference()} description <description>`." ;
2020-02-01 12:03:02 +00:00
2021-11-27 02:10:56 +00:00
if ( ctx . MatchRaw ( ) )
{
if ( target . Description = = null )
await ctx . Reply ( noDescriptionSetMessage ) ;
else
await ctx . Reply ( $"```\n{target.Description}\n```" ) ;
return ;
}
2020-02-01 12:03:02 +00:00
2021-11-27 02:10:56 +00:00
if ( ! ctx . HasNext ( false ) )
{
if ( target . Description = = null )
await ctx . Reply ( noDescriptionSetMessage ) ;
else
await ctx . Reply ( embed : new EmbedBuilder ( )
. Title ( "Member description" )
. Description ( target . Description )
. Field ( new Embed . Field ( "\u200B" ,
$"To print the description with formatting, type `pk;member {target.Reference()} description -raw`."
+ ( ctx . System ? . Id = = target . System
? $" To clear it, type `pk;member {target.Reference()} description -clear`."
: "" ) ) )
. Build ( ) ) ;
return ;
}
ctx . CheckOwnMember ( target ) ;
if ( await ctx . MatchClear ( "this member's description" ) )
{
var patch = new MemberPatch { Description = Partial < string > . Null ( ) } ;
2021-09-30 01:51:38 +00:00
await _repo . UpdateMember ( target . Id , patch ) ;
2021-11-27 02:10:56 +00:00
await ctx . Reply ( $"{Emojis.Success} Member description cleared." ) ;
}
else
{
var description = ctx . RemainderOrNull ( false ) . NormalizeLineEndSpacing ( ) ;
if ( description . IsLongerThan ( Limits . MaxDescriptionLength ) )
throw Errors . StringTooLongError ( "Description" , description . Length , Limits . MaxDescriptionLength ) ;
2020-02-01 12:03:02 +00:00
2021-11-27 02:10:56 +00:00
var patch = new MemberPatch { Description = Partial < string > . Present ( description ) } ;
await _repo . UpdateMember ( target . Id , patch ) ;
2020-02-01 12:03:02 +00:00
2021-11-27 02:10:56 +00:00
await ctx . Reply ( $"{Emojis.Success} Member description changed." ) ;
2020-02-01 12:03:02 +00:00
}
2021-11-27 02:10:56 +00:00
}
public async Task Pronouns ( Context ctx , PKMember target )
{
var noPronounsSetMessage = "This member does not have pronouns set." ;
if ( ctx . System ? . Id = = target . System )
noPronounsSetMessage + = $"To set some, type `pk;member {target.Reference()} pronouns <pronouns>`." ;
2020-03-04 17:13:36 +00:00
2021-12-07 06:32:29 +00:00
ctx . CheckSystemPrivacy ( target . System , target . PronounPrivacy ) ;
2021-11-27 02:10:56 +00:00
if ( ctx . MatchRaw ( ) )
2021-08-27 15:03:47 +00:00
{
2021-11-27 02:10:56 +00:00
if ( target . Pronouns = = null )
await ctx . Reply ( noPronounsSetMessage ) ;
else
await ctx . Reply ( $"```\n{target.Pronouns}\n```" ) ;
return ;
}
2020-06-29 11:57:48 +00:00
2021-11-27 02:10:56 +00:00
if ( ! ctx . HasNext ( false ) )
{
if ( target . Pronouns = = null )
await ctx . Reply ( noPronounsSetMessage ) ;
else
await ctx . Reply (
$"**{target.NameFor(ctx)}**'s pronouns are **{target.Pronouns}**.\nTo print the pronouns with formatting, type `pk;member {target.Reference()} pronouns -raw`."
+ ( ctx . System ? . Id = = target . System
? $" To clear them, type `pk;member {target.Reference()} pronouns -clear`."
: "" ) ) ;
return ;
}
2021-08-27 23:20:14 +00:00
2021-11-27 02:10:56 +00:00
ctx . CheckOwnMember ( target ) ;
2021-08-27 23:20:14 +00:00
2021-11-27 02:10:56 +00:00
if ( await ctx . MatchClear ( "this member's pronouns" ) )
{
var patch = new MemberPatch { Pronouns = Partial < string > . Null ( ) } ;
await _repo . UpdateMember ( target . Id , patch ) ;
await ctx . Reply ( $"{Emojis.Success} Member pronouns cleared." ) ;
}
else
{
var pronouns = ctx . RemainderOrNull ( false ) . NormalizeLineEndSpacing ( ) ;
if ( pronouns . IsLongerThan ( Limits . MaxPronounsLength ) )
throw Errors . StringTooLongError ( "Pronouns" , pronouns . Length , Limits . MaxPronounsLength ) ;
2020-02-27 23:23:54 +00:00
2021-11-27 02:10:56 +00:00
var patch = new MemberPatch { Pronouns = Partial < string > . Present ( pronouns ) } ;
await _repo . UpdateMember ( target . Id , patch ) ;
await ctx . Reply ( $"{Emojis.Success} Member pronouns changed." ) ;
}
}
2021-08-27 15:03:47 +00:00
2021-11-27 02:10:56 +00:00
public async Task BannerImage ( Context ctx , PKMember target )
{
ctx . CheckOwnMember ( target ) ;
2021-08-27 15:03:47 +00:00
2021-11-27 02:10:56 +00:00
async Task ClearBannerImage ( )
{
await _repo . UpdateMember ( target . Id , new MemberPatch { BannerImage = null } ) ;
await ctx . Reply ( $"{Emojis.Success} Member banner image cleared." ) ;
2020-02-01 12:03:02 +00:00
}
2021-08-27 15:03:47 +00:00
2021-11-27 02:10:56 +00:00
async Task SetBannerImage ( ParsedImage img )
2021-08-27 15:03:47 +00:00
{
2021-11-27 02:10:56 +00:00
await AvatarUtils . VerifyAvatarOrThrow ( _client , img . Url , true ) ;
2020-11-22 21:15:26 +00:00
2021-11-27 02:10:56 +00:00
await _repo . UpdateMember ( target . Id , new MemberPatch { BannerImage = img . Url } ) ;
2021-08-27 23:20:14 +00:00
2021-11-27 02:10:56 +00:00
var msg = img . Source switch
2020-03-04 17:13:36 +00:00
{
2021-11-27 02:10:56 +00:00
AvatarSource . Url = > $"{Emojis.Success} Member banner image changed to the image at the given URL." ,
AvatarSource . Attachment = >
$"{Emojis.Success} Member banner image changed to attached image.\n{Emojis.Warn} If you delete the message containing the attachment, the banner image will stop working." ,
AvatarSource . User = > throw new PKError ( "Cannot set a banner image to an user's avatar." ) ,
_ = > throw new ArgumentOutOfRangeException ( )
} ;
2020-02-01 12:03:02 +00:00
2021-11-27 02:10:56 +00:00
// The attachment's already right there, no need to preview it.
var hasEmbed = img . Source ! = AvatarSource . Attachment ;
await ( hasEmbed
? ctx . Reply ( msg , new EmbedBuilder ( ) . Image ( new Embed . EmbedImage ( img . Url ) ) . Build ( ) )
: ctx . Reply ( msg ) ) ;
}
2021-08-27 23:20:14 +00:00
2021-11-27 02:10:56 +00:00
async Task ShowBannerImage ( )
{
if ( ( target . BannerImage ? . Trim ( ) ? ? "" ) . Length > 0 )
2021-08-27 23:20:14 +00:00
{
2021-11-27 02:10:56 +00:00
var eb = new EmbedBuilder ( )
. Title ( $"{target.NameFor(ctx)}'s banner image" )
. Image ( new Embed . EmbedImage ( target . BannerImage ) )
. Description ( $"To clear, use `pk;member {target.Hid} banner clear`." ) ;
await ctx . Reply ( embed : eb . Build ( ) ) ;
2021-08-27 23:20:14 +00:00
}
else
{
2021-11-27 02:10:56 +00:00
throw new PKSyntaxError (
"This member does not have a banner image set. Set one by attaching an image to this command, or by passing an image URL or @mention." ) ;
2020-03-04 17:13:36 +00:00
}
2020-02-01 12:03:02 +00:00
}
2021-11-27 02:10:56 +00:00
if ( await ctx . MatchClear ( "this member's banner image" ) )
await ClearBannerImage ( ) ;
else if ( await ctx . MatchImage ( ) is { } img )
await SetBannerImage ( img ) ;
else
await ShowBannerImage ( ) ;
}
public async Task Color ( Context ctx , PKMember target )
{
var color = ctx . RemainderOrNull ( ) ;
if ( await ctx . MatchClear ( ) )
2021-08-02 17:46:12 +00:00
{
ctx . CheckOwnMember ( target ) ;
2021-11-27 02:10:56 +00:00
var patch = new MemberPatch { Color = Partial < string > . Null ( ) } ;
await _repo . UpdateMember ( target . Id , patch ) ;
2021-08-02 17:46:12 +00:00
2021-11-27 02:10:56 +00:00
await ctx . Reply ( $"{Emojis.Success} Member color cleared." ) ;
}
else if ( ! ctx . HasNext ( ) )
{
// if (!target.ColorPrivacy.CanAccess(ctx.LookupContextFor(target.System)))
// throw Errors.LookupNotAllowed;
2021-08-02 17:46:12 +00:00
2021-11-27 02:10:56 +00:00
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.Reference()} color <color>`." ) ;
2021-08-02 17:46:12 +00:00
else
2021-11-27 02:10:56 +00:00
await ctx . Reply ( "This member does not have a color set." ) ;
2021-08-02 17:46:12 +00:00
else
2021-11-27 02:10:56 +00:00
await ctx . Reply ( embed : new EmbedBuilder ( )
. Title ( "Member color" )
. Color ( target . Color . ToDiscordColor ( ) )
. Thumbnail ( new Embed . EmbedThumbnail ( $"https://fakeimg.pl/256x256/{target.Color}/?text=%20" ) )
. Description ( $"This member's color is **#{target.Color}**."
+ ( ctx . System ? . Id = = target . System
? $" To clear it, type `pk;member {target.Reference()} color -clear`."
: "" ) )
. Build ( ) ) ;
2021-08-02 17:46:12 +00:00
}
2021-11-27 02:10:56 +00:00
else
2020-02-01 12:03:02 +00:00
{
2021-11-27 02:10:56 +00:00
ctx . CheckOwnMember ( target ) ;
2021-08-27 15:03:47 +00:00
2021-11-27 02:10:56 +00:00
if ( color . StartsWith ( "#" ) ) color = color . Substring ( 1 ) ;
if ( ! Regex . IsMatch ( color , "^[0-9a-fA-F]{6}$" ) ) throw Errors . InvalidColorError ( color ) ;
2021-08-27 15:03:47 +00:00
2021-11-27 02:10:56 +00:00
var patch = new MemberPatch { Color = Partial < string > . Present ( color . ToLowerInvariant ( ) ) } ;
await _repo . UpdateMember ( target . Id , patch ) ;
2020-02-01 12:03:02 +00:00
2021-11-27 02:10:56 +00:00
await ctx . Reply ( embed : new EmbedBuilder ( )
. Title ( $"{Emojis.Success} Member color changed." )
. Color ( color . ToDiscordColor ( ) )
. Thumbnail ( new Embed . EmbedThumbnail ( $"https://fakeimg.pl/256x256/{color}/?text=%20" ) )
. Build ( ) ) ;
}
}
2021-08-27 15:03:47 +00:00
2021-11-27 02:10:56 +00:00
public async Task Birthday ( Context ctx , PKMember target )
{
if ( await ctx . MatchClear ( "this member's birthday" ) )
{
ctx . CheckOwnMember ( target ) ;
2020-03-04 17:13:36 +00:00
2021-11-27 02:10:56 +00:00
var patch = new MemberPatch { Birthday = Partial < LocalDate ? > . Null ( ) } ;
await _repo . UpdateMember ( target . Id , patch ) ;
await ctx . Reply ( $"{Emojis.Success} Member birthdate cleared." ) ;
2020-02-01 12:03:02 +00:00
}
2021-11-27 02:10:56 +00:00
else if ( ! ctx . HasNext ( ) )
2020-02-01 12:03:02 +00:00
{
2021-12-07 06:32:29 +00:00
ctx . CheckSystemPrivacy ( target . System , target . BirthdayPrivacy ) ;
2021-08-27 15:03:47 +00:00
2021-11-27 02:10:56 +00:00
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.Reference()} birthdate <birthdate>`."
: "" ) ) ;
else
await ctx . Reply ( $"This member's birthdate is **{target.BirthdayString}**."
+ ( ctx . System ? . Id = = target . System
? $" To clear it, type `pk;member {target.Reference()} birthdate -clear`."
: "" ) ) ;
}
else
{
ctx . CheckOwnMember ( target ) ;
2020-06-29 11:57:48 +00:00
2021-11-27 02:10:56 +00:00
var birthdayStr = ctx . RemainderOrNull ( ) ;
2021-08-27 15:03:47 +00:00
2021-11-27 02:10:56 +00:00
LocalDate ? birthday ;
if ( birthdayStr = = "today" | | birthdayStr = = "now" )
2021-11-30 02:35:21 +00:00
birthday = SystemClock . Instance . InZone ( ctx . Zone ) . GetCurrentDate ( ) ;
2020-03-04 17:13:36 +00:00
else
2021-11-27 02:10:56 +00:00
birthday = DateUtils . ParseDate ( birthdayStr , true ) ;
2021-08-27 15:03:47 +00:00
2021-11-27 02:10:56 +00:00
if ( birthday = = null ) throw Errors . BirthdayParseError ( birthdayStr ) ;
2021-10-29 21:44:51 +00:00
2021-11-27 02:10:56 +00:00
var patch = new MemberPatch { Birthday = Partial < LocalDate ? > . Present ( birthday ) } ;
await _repo . UpdateMember ( target . Id , patch ) ;
2021-10-29 21:44:51 +00:00
2021-11-27 02:10:56 +00:00
await ctx . Reply ( $"{Emojis.Success} Member birthdate changed." ) ;
}
}
2021-08-27 15:03:47 +00:00
2021-11-27 02:10:56 +00:00
private async Task < EmbedBuilder > CreateMemberNameInfoEmbed ( Context ctx , PKMember target )
{
2021-12-06 05:32:54 +00:00
var lcx = ctx . LookupContextFor ( target . System ) ;
2020-06-29 11:57:48 +00:00
2021-11-27 02:10:56 +00:00
MemberGuildSettings memberGuildConfig = null ;
if ( ctx . Guild ! = null )
memberGuildConfig = await _repo . GetMemberGuild ( ctx . Guild . Id , target . Id ) ;
2021-08-27 15:03:47 +00:00
2021-11-27 02:10:56 +00:00
var eb = new EmbedBuilder ( )
. Title ( "Member names" )
. Footer ( new Embed . EmbedFooter (
$"Member ID: {target.Hid} | Active name in bold. Server name overrides display name, which overrides base name." ) ) ;
2021-08-27 15:03:47 +00:00
2021-11-27 02:10:56 +00:00
if ( target . DisplayName = = null & & memberGuildConfig ? . DisplayName = = null )
eb . Field ( new Embed . Field ( "Name" , $"**{target.NameFor(ctx)}**" ) ) ;
else
eb . Field ( new Embed . Field ( "Name" , target . NameFor ( ctx ) ) ) ;
2020-02-01 12:03:02 +00:00
2021-11-27 02:10:56 +00:00
if ( target . NamePrivacy . CanAccess ( lcx ) )
{
if ( target . DisplayName ! = null & & memberGuildConfig ? . DisplayName = = null )
eb . Field ( new Embed . Field ( "Display Name" , $"**{target.DisplayName}**" ) ) ;
else
eb . Field ( new Embed . Field ( "Display Name" , target . DisplayName ? ? "*(none)*" ) ) ;
}
2020-02-01 12:03:02 +00:00
2021-11-27 02:10:56 +00:00
if ( ctx . Guild ! = null )
{
if ( memberGuildConfig ? . DisplayName ! = null )
eb . Field ( new Embed . Field ( $"Server Name (in {ctx.Guild.Name})" ,
$"**{memberGuildConfig.DisplayName}**" ) ) ;
2020-03-04 17:13:36 +00:00
else
2021-11-27 02:10:56 +00:00
eb . Field ( new Embed . Field ( $"Server Name (in {ctx.Guild.Name})" ,
memberGuildConfig ? . DisplayName ? ? "*(none)*" ) ) ;
}
2020-06-18 15:08:36 +00:00
2021-11-27 02:10:56 +00:00
return eb ;
}
2020-02-01 12:03:02 +00:00
2021-11-27 02:10:56 +00:00
public async Task DisplayName ( Context ctx , PKMember target )
{
async Task PrintSuccess ( string text )
{
var successStr = text ;
2021-01-31 15:16:52 +00:00
if ( ctx . Guild ! = null )
2020-02-01 12:03:02 +00:00
{
2021-11-27 02:10:56 +00:00
var memberGuildConfig = await _repo . GetMemberGuild ( ctx . Guild . Id , target . Id ) ;
if ( memberGuildConfig . DisplayName ! = null )
successStr + =
$" However, this member has a server name set in this server ({ctx.Guild.Name}), and will be proxied using that name, \" { memberGuildConfig . DisplayName } \ ", here." ;
2020-02-01 12:03:02 +00:00
}
2021-11-27 02:10:56 +00:00
await ctx . Reply ( successStr ) ;
2020-02-01 12:03:02 +00:00
}
2021-11-27 02:10:56 +00:00
var noDisplayNameSetMessage = "This member does not have a display name set." ;
if ( ctx . System ? . Id = = target . System )
noDisplayNameSetMessage + =
$" To set one, type `pk;member {target.Reference()} displayname <display name>`." ;
2021-08-27 15:03:47 +00:00
2021-11-27 02:10:56 +00:00
// No perms check, display name isn't covered by member privacy
2021-08-27 23:20:14 +00:00
2021-11-27 02:10:56 +00:00
if ( ctx . MatchRaw ( ) )
{
if ( target . DisplayName = = null )
await ctx . Reply ( noDisplayNameSetMessage ) ;
2020-03-04 17:13:36 +00:00
else
2021-11-27 02:10:56 +00:00
await ctx . Reply ( $"```\n{target.DisplayName}\n```" ) ;
return ;
}
2020-03-04 17:13:36 +00:00
2021-11-27 02:10:56 +00:00
if ( ! ctx . HasNext ( false ) )
{
var eb = await CreateMemberNameInfoEmbed ( ctx , target ) ;
if ( ctx . System ? . Id = = target . System )
eb . Description (
$"To change display name, type `pk;member {target.Reference()} displayname <display name>`."
+ $"To clear it, type `pk;member {target.Reference()} displayname -clear`."
+ $"To print the raw display name, type `pk;member {target.Reference()} displayname -raw`." ) ;
await ctx . Reply ( embed : eb . Build ( ) ) ;
return ;
2020-03-04 17:13:36 +00:00
}
2021-08-27 15:03:47 +00:00
2021-11-27 02:10:56 +00:00
ctx . CheckOwnMember ( target ) ;
if ( await ctx . MatchClear ( "this member's display name" ) )
2020-03-04 17:13:36 +00:00
{
2021-11-27 02:10:56 +00:00
var patch = new MemberPatch { DisplayName = Partial < string > . Null ( ) } ;
await _repo . UpdateMember ( target . Id , patch ) ;
2021-08-27 15:03:47 +00:00
2021-11-27 02:10:56 +00:00
await PrintSuccess (
2022-01-15 03:30:02 +00:00
$"{Emojis.Success} Member display name cleared. This member will now be proxied using their member name \" { target . Name } \ "." ) ;
if ( target . NamePrivacy = = PrivacyLevel . Private )
await ctx . Reply ( $"{Emojis.Warn} Since this member no longer has a display name set, their name privacy **can no longer take effect**." ) ;
2021-11-27 02:10:56 +00:00
}
else
{
var newDisplayName = ctx . RemainderOrNull ( false ) . NormalizeLineEndSpacing ( ) ;
2021-08-27 23:20:14 +00:00
2021-11-27 02:10:56 +00:00
var patch = new MemberPatch { DisplayName = Partial < string > . Present ( newDisplayName ) } ;
await _repo . UpdateMember ( target . Id , patch ) ;
2021-08-27 23:20:14 +00:00
2021-11-27 02:10:56 +00:00
await PrintSuccess (
$"{Emojis.Success} Member display name changed. This member will now be proxied using the name \" { newDisplayName } \ "." ) ;
}
}
2020-06-13 14:03:57 +00:00
2021-11-27 02:10:56 +00:00
public async Task ServerName ( Context ctx , PKMember target )
{
ctx . CheckGuildContext ( ) ;
2021-08-27 23:20:14 +00:00
2021-11-27 02:10:56 +00:00
var noServerNameSetMessage = "This member does not have a server name set." ;
if ( ctx . System ? . Id = = target . System )
noServerNameSetMessage + =
$" To set one, type `pk;member {target.Reference()} servername <server name>`." ;
2021-08-27 23:20:14 +00:00
2021-11-27 02:10:56 +00:00
// No perms check, display name isn't covered by member privacy
var memberGuildConfig = await _repo . GetMemberGuild ( ctx . Guild . Id , target . Id ) ;
2020-02-01 12:03:02 +00:00
2021-11-27 02:10:56 +00:00
if ( ctx . MatchRaw ( ) )
{
if ( memberGuildConfig . DisplayName = = null )
await ctx . Reply ( noServerNameSetMessage ) ;
2020-02-01 12:03:02 +00:00
else
2021-11-27 02:10:56 +00:00
await ctx . Reply ( $"```\n{memberGuildConfig.DisplayName}\n```" ) ;
return ;
}
2020-02-01 12:03:02 +00:00
2021-11-27 02:10:56 +00:00
if ( ! ctx . HasNext ( false ) )
{
var eb = await CreateMemberNameInfoEmbed ( ctx , target ) ;
if ( ctx . System ? . Id = = target . System )
eb . Description (
$"To change server name, type `pk;member {target.Reference()} servername <server name>`.\nTo clear it, type `pk;member {target.Reference()} servername -clear`.\nTo print the raw server name, type `pk;member {target.Reference()} servername -raw`." ) ;
await ctx . Reply ( embed : eb . Build ( ) ) ;
return ;
2020-02-01 12:03:02 +00:00
}
2021-08-27 15:03:47 +00:00
2021-11-27 02:10:56 +00:00
ctx . CheckOwnMember ( target ) ;
if ( await ctx . MatchClear ( "this member's server name" ) )
2020-02-01 12:03:02 +00:00
{
2021-11-27 02:10:56 +00:00
await _repo . UpdateMemberGuild ( target . Id , ctx . Guild . Id , new MemberGuildPatch { DisplayName = null } ) ;
2020-02-01 12:03:02 +00:00
2021-11-27 02:10:56 +00:00
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 } \ " in this server ({ctx.Guild.Name})." ) ;
2020-03-04 17:13:36 +00:00
else
2021-11-27 02:10:56 +00:00
await ctx . Reply (
$"{Emojis.Success} Member server name cleared. This member will now be proxied using their member name \" { target . NameFor ( ctx ) } \ " in this server ({ctx.Guild.Name})." ) ;
}
else
{
var newServerName = ctx . RemainderOrNull ( false ) . NormalizeLineEndSpacing ( ) ;
2020-02-01 12:03:02 +00:00
2021-11-27 02:10:56 +00:00
await _repo . UpdateMemberGuild ( target . Id , ctx . Guild . Id ,
new MemberGuildPatch { DisplayName = newServerName } ) ;
2021-08-27 15:03:47 +00:00
2021-11-27 02:10:56 +00:00
await ctx . Reply (
$"{Emojis.Success} Member server name changed. This member will now be proxied using the name \" { newServerName } \ " in this server ({ctx.Guild.Name})." ) ;
2020-02-01 12:03:02 +00:00
}
2021-11-27 02:10:56 +00:00
}
2020-02-01 12:03:02 +00:00
2021-11-27 02:10:56 +00:00
public async Task KeepProxy ( Context ctx , PKMember target )
{
ctx . CheckSystem ( ) . CheckOwnMember ( target ) ;
2020-11-20 21:40:36 +00:00
2021-11-27 02:10:56 +00:00
bool newValue ;
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
{
if ( target . KeepProxy )
await ctx . Reply (
"This member has keepproxy **enabled**, which means proxy tags will be **included** in the resulting message when proxying." ) ;
2020-11-20 21:40:36 +00:00
else
2021-11-27 02:10:56 +00:00
await ctx . Reply (
"This member has keepproxy **disabled**, which means proxy tags will **not** be included in the resulting message when proxying." ) ;
return ;
}
2020-11-20 21:40:36 +00:00
2021-11-27 02:10:56 +00:00
;
var patch = new MemberPatch { KeepProxy = Partial < bool > . Present ( newValue ) } ;
await _repo . UpdateMember ( target . Id , patch ) ;
if ( newValue )
await ctx . Reply (
$"{Emojis.Success} Member proxy tags will now be included in the resulting message when proxying." ) ;
else
await ctx . Reply (
$"{Emojis.Success} Member proxy tags will now not be included in the resulting message when proxying." ) ;
}
public async Task MemberAutoproxy ( Context ctx , PKMember target )
{
if ( ctx . System = = null ) throw Errors . NoSystemError ;
if ( target . System ! = ctx . System . Id ) throw Errors . NotOwnMemberError ;
2020-11-20 21:40:36 +00:00
2021-11-27 02:10:56 +00:00
bool newValue ;
// todo: MatchToggle
if ( ctx . Match ( "on" , "enabled" , "true" , "yes" ) | | ctx . MatchFlag ( "on" , "enabled" , "true" , "yes" ) )
{
newValue = true ;
}
else if ( ctx . Match ( "off" , "disabled" , "false" , "no" ) | | ctx . MatchFlag ( "off" , "disabled" , "false" , "no" ) )
{
newValue = false ;
}
else if ( ctx . HasNext ( ) )
{
throw new PKSyntaxError ( "You must pass either \"on\" or \"off\"." ) ;
}
else
{
if ( target . AllowAutoproxy )
await ctx . Reply (
"Latch/front autoproxy are **enabled** for this member. This member will be automatically proxied when autoproxy is set to latch or front mode." ) ;
2020-11-20 21:40:36 +00:00
else
2021-11-27 02:10:56 +00:00
await ctx . Reply (
"Latch/front autoproxy are **disabled** for this member. This member will not be automatically proxied when autoproxy is set to latch or front mode." ) ;
return ;
2020-11-20 21:40:36 +00:00
}
2021-11-27 02:10:56 +00:00
;
2020-02-01 12:03:02 +00:00
2021-11-27 02:10:56 +00:00
var patch = new MemberPatch { AllowAutoproxy = Partial < bool > . Present ( newValue ) } ;
await _repo . UpdateMember ( target . Id , patch ) ;
2021-08-27 15:03:47 +00:00
2021-11-27 02:10:56 +00:00
if ( newValue )
await ctx . Reply ( $"{Emojis.Success} Latch / front autoproxy have been **enabled** for this member." ) ;
else
await ctx . Reply ( $"{Emojis.Success} Latch / front autoproxy have been **disabled** for this member." ) ;
}
2020-02-01 12:03:02 +00:00
2021-11-27 02:10:56 +00:00
public async Task Privacy ( Context ctx , PKMember target , PrivacyLevel ? newValueFromCommand )
{
ctx . CheckSystem ( ) . CheckOwnMember ( target ) ;
2021-08-27 15:03:47 +00:00
2021-11-27 02:10:56 +00:00
// Display privacy settings
if ( ! ctx . HasNext ( ) & & newValueFromCommand = = null )
{
await ctx . Reply ( embed : new EmbedBuilder ( )
. Title ( $"Current privacy settings for {target.NameFor(ctx)}" )
. Field ( new Embed . Field ( "Name (replaces name with display name if member has one)" ,
target . NamePrivacy . Explanation ( ) ) )
. Field ( new Embed . Field ( "Description" , target . DescriptionPrivacy . Explanation ( ) ) )
. Field ( new Embed . Field ( "Avatar" , target . AvatarPrivacy . Explanation ( ) ) )
. Field ( new Embed . Field ( "Birthday" , target . BirthdayPrivacy . Explanation ( ) ) )
. Field ( new Embed . Field ( "Pronouns" , target . PronounPrivacy . Explanation ( ) ) )
. Field ( new Embed . Field ( "Meta (message count, last front, last message)" ,
target . MetadataPrivacy . Explanation ( ) ) )
. Field ( new Embed . Field ( "Visibility" , target . MemberVisibility . Explanation ( ) ) )
. Description (
"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`." )
. Build ( ) ) ;
return ;
}
2020-07-07 22:46:58 +00:00
2021-11-27 02:10:56 +00:00
// Get guild settings (mostly for warnings and such)
MemberGuildSettings guildSettings = null ;
if ( ctx . Guild ! = null )
guildSettings = await _repo . GetMemberGuild ( ctx . Guild . Id , target . Id ) ;
2020-07-07 22:46:58 +00:00
2021-11-27 02:10:56 +00:00
async Task SetAll ( PrivacyLevel level )
{
await _repo . UpdateMember ( target . Id , new MemberPatch ( ) . WithAllPrivacy ( level ) ) ;
if ( level = = PrivacyLevel . Private )
await ctx . Reply (
$"{Emojis.Success} All {target.NameFor(ctx)}'s privacy settings have been set to **{level.LevelName()}**. Other accounts will now see nothing on the member card." ) ;
2020-02-01 12:03:02 +00:00
else
2021-11-27 02:10:56 +00:00
await ctx . Reply (
$"{Emojis.Success} All {target.NameFor(ctx)}'s privacy settings have been set to **{level.LevelName()}**. Other accounts will now see everything on the member card." ) ;
2020-02-01 12:03:02 +00:00
}
2021-08-27 15:03:47 +00:00
2021-11-27 02:10:56 +00:00
async Task SetLevel ( MemberPrivacySubject subject , PrivacyLevel level )
2020-02-01 12:03:02 +00:00
{
2021-11-27 02:10:56 +00:00
await _repo . UpdateMember ( target . Id , new MemberPatch ( ) . WithPrivacy ( subject , level ) ) ;
var subjectName = subject switch
{
MemberPrivacySubject . Name = > "name privacy" ,
MemberPrivacySubject . Description = > "description privacy" ,
MemberPrivacySubject . Avatar = > "avatar privacy" ,
MemberPrivacySubject . Pronouns = > "pronoun privacy" ,
MemberPrivacySubject . Birthday = > "birthday privacy" ,
MemberPrivacySubject . Metadata = > "metadata privacy" ,
MemberPrivacySubject . Visibility = > "visibility" ,
_ = > throw new ArgumentOutOfRangeException ( $"Unknown privacy subject {subject}" )
} ;
var explanation = ( subject , level ) 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}, {level})" )
} ;
2021-08-27 15:03:47 +00:00
2021-11-27 02:10:56 +00:00
await ctx . Reply (
$"{Emojis.Success} {target.NameFor(ctx)}'s **{subjectName}** has been set to **{level.LevelName()}**. {explanation}" ) ;
2021-08-27 15:03:47 +00:00
2021-11-27 02:10:56 +00:00
// Name privacy only works given a display name
if ( subject = = MemberPrivacySubject . Name & & level = = 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**." ) ;
2021-08-27 15:03:47 +00:00
2021-11-27 02:10:56 +00:00
// Avatar privacy doesn't apply when proxying if no server avatar is set
if ( subject = = MemberPrivacySubject . Avatar & & level = = 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.Reference()} serveravatar`" ) ;
2020-02-01 12:03:02 +00:00
}
2021-11-27 02:10:56 +00:00
if ( ctx . Match ( "all" ) | | newValueFromCommand ! = null )
await SetAll ( newValueFromCommand ? ? ctx . PopPrivacyLevel ( ) ) ;
else
await SetLevel ( ctx . PopMemberPrivacySubject ( ) , ctx . PopPrivacyLevel ( ) ) ;
}
public async Task Delete ( Context ctx , PKMember target )
{
ctx . CheckSystem ( ) . CheckOwnMember ( target ) ;
await ctx . Reply (
$"{Emojis.Warn} Are you sure you want to delete \" { target . NameFor ( ctx ) } \ "? 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 _repo . DeleteMember ( target . Id ) ;
await ctx . Reply ( $"{Emojis.Success} Member deleted." ) ;
2020-02-01 12:03:02 +00:00
}
2021-08-27 15:03:47 +00:00
}